package main.java.LeetCode.SolutionVersionTwo;

import java.util.ArrayDeque;
import java.util.HashMap;

@SuppressWarnings("All")
public class LRUCache {
    static class DLinkedNode{
        String key;
        int val;
        DLinkedNode pre;
        DLinkedNode next;
        public DLinkedNode(String key,int val){
            this.key = key;
            this.val = val;
        }
    }
    private HashMap<String,DLinkedNode> cache;
    private int counter;
    private int capacity;
    private DLinkedNode head,tail;

    public LRUCache(int capacity){
        this.counter = 0;
        this.capacity = capacity;
        this.cache = new HashMap<String,DLinkedNode>();
        this.head = new DLinkedNode("head",0);
        this.tail = new DLinkedNode("tail",-1);
        head.next = tail;
        tail.pre = head;
    }

    public int get(String key){
        if(cache.get(key)==null)
            return -1;
        return cache.get(key).val;
    }

    public void put(String key,int value){
        DLinkedNode node = cache.get(key);
        if(node!=null){
            node.val = value;
            removeNodeByKey(node);
            addNodeToHead(node);
            return;
        }
        node = new DLinkedNode(key,value);
        cache.put(node.key,node);
        addNodeToHead(node);
        counter++;
        if(counter>capacity)
        {
            removeNodeFromTail();
            counter--;
        }
        return;
    }

    private void addNodeToHead(DLinkedNode node){
        DLinkedNode temp = head.next;
        head.next = node;
        node.pre = head;
        node.next = temp;
        temp.pre = node;
    }
    private void removeNodeFromTail(){
        if(counter==0)
            return;
        DLinkedNode temp = tail.pre.pre;
        tail.pre.next = null;
        tail.pre.pre = null;
        tail.pre = temp;
        temp.next = tail;
    }
    private void removeNodeByKey(DLinkedNode node){
        DLinkedNode nextNode = node.next;
        DLinkedNode preNode = node.pre;
        node.pre = null;
        node.next = null;
        preNode.next = nextNode;
        nextNode.pre = preNode;
        return;
    }

    public DLinkedNode getHead(){
        return this.head;
    }

}
